package java2plant.builder;

import java2plant.describer.*;

import java.io.*;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 
 * @author arthur
 */
public class FromJavaBuilder extends AbstractBuilder {

    public FromJavaBuilder() {

        this.context = ContextDescriber.getInstance();
    }

    @Override
    public ContextDescriber buildFromFile(File fInputDir) {

        try {
            ArrayList<File> files = new ArrayList();
            ArrayList<File> dirs = new ArrayList();

            if (fInputDir.isDirectory()) {
                dirs.add(fInputDir);
            } else {
                files.add(fInputDir);
            }

            for (int i = 0; i < dirs.size(); i++) {
                File[] childs = dirs.get(i).listFiles();
                for (File child : childs) {
                    if (child.isDirectory()) {
                        dirs.add(child);
                    } else if (child.getName().endsWith(".java")) {
                        files.add(child);
                    }
                }
            }
            for (File f : files)
                System.out.println(f.getAbsolutePath() + " " + f.getName());

            for (File f : files) {
                FileInputStream fis = new FileInputStream(f);
                AbstractBuilder builder = new FromJavaBuilder();
                buildFromStream(fis);
            }

        } catch (Exception ex) {
            Logger.getLogger(FromJavaBuilder.class.getName()).log(Level.SEVERE,
                    null, ex);
        }

        return context;

    }

    public ContextDescriber buildFromStream(InputStream inputStream) {

        this.is = inputStream;

        String str = getNext(is);
        String decla = extractDeclaration(str);
        while (!str.isEmpty()) {

            if (decla.contains("package ")) {
                String[] split = splitString(decla);
                for (int i = 0; i < split.length; i++) {
                    if (split[i].contentEquals("package")) {
                        context.setNamespace(split[i + 1]);
                    }
                }
            } else if (decla.contains(" class ")) {
                buildClassFromString(str);
                // TODO: pas constistant avec le reste (pas de add)
            } else if (str.contains(" interface ")) {
                buildClassFromString(str);
            }
            str = getNext(is);
            decla = extractDeclaration(str);
        }

        return context;
    }

    public String getNext(InputStream is) {

        String str = "";
        try {
            int cOld = 0;
            int cNew = 0;
            boolean parsing = true;
            int openedBraces = 0;

            cNew = is.read();
            if (cNew == -1) {
                throw new EOFException();
            }

            while (parsing) {
                cOld = cNew;
                cNew = is.read();
                if (cNew == '\"' && cOld != '\\') {
                    cOld = cNew;
                    cNew = is.read();
                    while (cNew != '\"' || cOld == '\\') {
                        if (cOld == '\\') {
                            // dont take care of the escaped char
                            cNew = ' ';
                        }
                        cOld = cNew;
                        cNew = is.read();
                    }
                    cOld = cNew;
                    cNew = is.read();

                } else if (cNew == '\'' && cOld != '\\') {
                    cOld = cNew;
                    cNew = is.read();
                    while (cNew != '\'' || cOld == '\\') {
                        if (cOld == '\\') {
                            // dont take care of the escaped char
                            cNew = ' ';
                        }
                        cOld = cNew;
                        cNew = is.read();
                    }
                    cOld = cNew;
                    cNew = is.read();
                } else if (cOld == '/' && cNew == '*') {
                    while (cOld != '*' || cNew != '/') {
                        cOld = cNew;
                        cNew = is.read();
                    }
                    cOld = cNew;
                    cNew = is.read();

                } else if (cOld == '/' && cNew == '/') {
                    while (cNew != '\n') {
                        cOld = cNew;
                        cNew = is.read();
                    }
                    cOld = cNew;
                    cNew = is.read();
                } else {
                    if (cNew == -1) {
                        parsing = false;
                    } else if (cNew == '{') {
                        openedBraces++;
                    } else if (cNew == '}') {
                        openedBraces--;
                        if (openedBraces == 0) {
                            parsing = false;
                        }
                    } else if (cNew == ';' && openedBraces == 0) {
                        parsing = false;
                    }
                    str += (char) cOld;
                }

            }
            str += (char) cNew;
        } catch (EOFException ex) {
        } catch (IOException ex) {
            Logger.getLogger(FromJavaBuilder.class.getName()).log(Level.SEVERE,
                    null, ex);
        }
        return str;
    }

    public String getNext(String src) {

        String next = "";

        if (src.indexOf(";") != -1
                && (src.indexOf(";") < src.indexOf("{") || src.indexOf("{") == -1)) {
            next = src.substring(0, src.indexOf(";") + 1);
        } else if (src.indexOf("{") != -1) {
            int openedBraces = 1;
            int i = src.indexOf("{") + 1;
            while (openedBraces > 0) {
                char c = src.charAt(i);
                if (c == '{') {
                    openedBraces++;
                } else if (c == '}') {
                    openedBraces--;
                }
                i++;
            }
            next = src.substring(0, i);
        }

        return next;
    }

    public String extractDeclaration(String str) {

        String declaration = "";
        if (str.indexOf(";") != -1 && str.indexOf("{") != -1) {
            if (str.indexOf(";") < str.indexOf("{")) {
                declaration = str.substring(0, str.indexOf(";") + 1);
            } else {
                declaration = str.substring(0, str.indexOf("{") + 1);
            }
        } else if (str.indexOf("{") == -1) {
            declaration = str.substring(0, str.indexOf(";") + 1);
        } else if (str.indexOf(";") == -1) {
            declaration = str.substring(0, str.indexOf("{") + 1);
        }
        return declaration;
    }

    public ClassDescriber buildClassFromString(String str) {

        ClassDescriber cd = null;

        String declaration = extractDeclaration(str);
        String split[] = splitString(extractDeclaration(str));

        for (int i = 0; i < split.length; i++) {
            if (split[i].equals("class")) {
                i++;
                cd = context.getClass(context.getNamespace(), split[i]);
            } else if (split[i].equals("interface")) {
                i++;
                cd = context.getClass(context.getNamespace(), split[i]);
                cd.setInterface(true);
            }
        }

        cd.setPackage(context.getNamespace());

        for (int i = 0; i < split.length; i++) {
            if (split[i].equals("public") || split[i].equals("private")
                    || split[i].equals("protected")
                    || split[i].equals("package")) {
                cd.setVisibility(split[i]);
            } else if (split[i].equals("abstract")) {
                cd.setAbstract(true);
            } else if (split[i].equals("extends")) {
                i++;
                for (; i < split.length && !split[i].equals("implements"); i++) {
                    cd.addInheritance(split[i].replace(",", ""));
                }
            } else if (split[i].equals("implements")) {
                i++;
                for (; i < split.length && !split[i].equals("extends"); i++) {
                    cd.addInheritance(split[i].replace(",", ""));
                }
            }
        }

        if (declaration.endsWith("{")) {
            str = str.substring(declaration.length(), str.lastIndexOf("}"));
        } else {
            str = "";
        }

        while (!str.isEmpty()) {
            // 处理supressWarnings换行的情况
            if (str.contains("SuppressWarnings")) {
                str = str.replace("@SuppressWarnings( {, } )", "@SuppressWarnings( )");
                str = str.replace("@SuppressWarnings( { } )", "@SuppressWarnings( )");
                str = str.replace("@SuppressWarnings({ })", "@SuppressWarnings( )");
                str = str.replace("@SuppressWarnings({, })", "@SuppressWarnings( )");
                str = str.replace("@SuppressWarnings)", "@SuppressWarnings( )");
                str = str.replace("@SuppressWarnings(})", "@SuppressWarnings( )");
            }

            // 处理@Param注解
            if (str.contains("@Param")) {
                str = str.replace("@Param)", "");
            }
            // 处理 @Transaction注解的解析
            if (str.startsWith(",")) {
                if (str.contains("propagation")) {
                    str = str.replace(",propagation = Propagation.NOT_SUPPORTED, readOnly = true)", "");
                }
            }

            // 处理 ） 开头的情况
            if (str.startsWith(")")) {
                str = str.substring(1,str.length());
            }

            String current = getNext(str);
            declaration = extractDeclaration(current);

            if (current.isEmpty()) {
                str = "";
            } else if (current.endsWith(";") && declaration.contains("=")) {
                FieldDescriber field = buildFieldFromString(current);
                cd.addField(field);
            } else if (current.endsWith(";") && !declaration.contains("(")) {
                FieldDescriber field = buildFieldFromString(current);
                cd.addField(field);
            } else if (current.contains("class") || current.contains("enum")
                    || current.contains("Requirement") || current.contains("interface")
                    || current.contains("static\n")) {
                str = str.substring(current.length());
                continue;
            } else if(current.contains("public static final") || current.contains("private static final")) {
                FieldDescriber field = buildFieldFromString(current);
                cd.addField(field);
            } else if(current.contains("@NotBlank") || current.contains("@NotNull")) {
                current = current.replace("@NotBlank(message)", "");
                current = current.replace("@NotNull(message)", "");

                FieldDescriber field = buildFieldFromString(current);
                cd.addField(field);

            } else {
                MethodDescriber method = buildMethodFromString(declaration);
                cd.addMethod(method);
            }
            str = str.substring(current.length());
        }

        return cd;
    }

    public MethodDescriber buildMethodFromString(String str) {
        // 去掉多余注解@Named()
        str = str.replace("@Named( )", "");

        MethodDescriber md = new MethodDescriber();
        System.out.println("MethodBuilder : " + str);
        String[] split = splitString(str);
        int i = 0;
        while (i < split.length) {
            if (split[i].equals("public") || split[i].equals("private")
                    || split[i].equals("protected")
                    || split[i].equals("package")) {
                md.setVisibility(split[i]);
                i++;
            } else if (split[i].equals("static")) {
                md.setStatic(true);
                i++;
            } else if (split[i].contains("final")) {
                i++;
            } else if (split[i].contains("abstract")) {
                md.setAbstract(true);
                i++;
            } else if (split[i].startsWith("@")) {
                i++;
            } else {
                if (split[i].contains("(")) {
                    md.setReturnType("");
                } else {
                    md.setReturnType(split[i]);
                    i++;
                }
                if (split[i].contains("(")) {
                    md.setName(split[i].substring(0, split[i].indexOf('(')));
                } else {
                    md.setName(split[i]);
                }
                i = split.length; // exit
            }

        }
        /* Construction des arguments */
        int a = str.indexOf("(");
        int b = str.indexOf(")");

        str = str.replace("@SuppressWarnings( )", "");
        str = str.substring(str.indexOf("(") + 1, str.indexOf(")"));

        if (!str.isEmpty()) {
            if (!str.trim().equals("")) {
                split = splitString(str, ",");
                for (int j = 0; j < split.length; j++) {
                    ArgumentDescriber arg = buildArgumentFromString(split[j]);
                    md.addArg(arg);
                }
            }

        }
        return md;
    }

    public ArgumentDescriber buildArgumentFromString(String str) {

        ArgumentDescriber ad = new ArgumentDescriber();
        System.out.println("ArgDescriber : " + str);

        String[] split = splitString(str);
        int i = 0;
        while (i < split.length && split[i].isEmpty()) {
            i++;
        }
        ad.setType(split[i]);
        i++;
        while (i < split.length && split[i].isEmpty()) {
            i++;
        }
        ad.setName(split[i]);

        return ad;
    }

    public FieldDescriber buildFieldFromString(String str) {

        FieldDescriber fd = new FieldDescriber();
        if (str.contains("HashMap")) {
            System.out.println("Field Builder : " + str);
        }
        str = str.replace(";", "");

        String[] split = splitString(str);
        int i = 0;
        while (i < split.length) {
            if (split[i].equals("public") || split[i].equals("private")
                    || split[i].equals("protected")
                    || split[i].equals("package")) {
                fd.setVisibility(split[i]);
                i++;
            } else if (split[i].contains("final")) {
                i++;
            } else if (split[i].contains("static")) {
                fd.setStatic(true);
                i++;
            } else {
                fd.setType(split[i]);
                fd.setName(split[i + 1]);
                i = split.length; // exit
            }
        }
        return fd;
    }

}
